import serial
import time
import re
import struct

def send_and_receive(port='COM8', baudrate=115200, hex_cmd='4A 54 41 0D', timeout=20, response_size=1024):
    """
    打开指定串口，发送 HEX 命令，并读取返回数据，遇到 0x0D 时停止。
    :param port: 串口号，例如 'COM8'
    :param baudrate: 波特率，例如 9600、115200
    :param hex_cmd: 要发送的 HEX 命令字符串，字节间用空格分隔
    :param timeout: 读取超时时间（秒），仅对 `read()` 有效
    :param response_size: 最大读取字节数
    """
    try:
        with serial.Serial(port=port, baudrate=baudrate, timeout=timeout) as ser:
            # 发送命令
            cmd_bytes = bytes.fromhex(hex_cmd)
            ser.write(cmd_bytes)

            # 等待设备响应
            time.sleep(0.05)

            # 循环接收直到 0x0D 或超时
            response = bytearray()
            start_time = time.time()

            while True:
                if ser.in_waiting:
                    byte = ser.read(1)
                    if not byte:
                        break
                    response += byte
                    # 检查是否遇到 0x0D
                    if byte == b'\x0D':
                        break
                    # 防止过长响应
                    if len(response) >= response_size:
                        break
                # 检查超时
                if time.time() - start_time > timeout:
                    break

            if response:
                hex_resp = response.hex().upper()
                ascii_resp = response.decode('ascii', errors='replace')
                return hex_resp, ascii_resp
            else:
                return 0x00, "Response Timeout"

    except serial.SerialException as e:
        return 0x00, f"Serial error: {e}"





class CA527Sensor:
    def __init__(self, port='COM8', baudrate=38400):
        self.port = port
        self.baudrate = baudrate

    def calibrate_sensor(self, timeout=20):
        """发送校准命令"""
        return send_and_receive(port=self.port, baudrate=self.baudrate, hex_cmd='5A 52 43 0D', timeout=timeout)

    def XYZmode(self, timeout=1):
        """切换到色坐标 (XYZ) 模式"""
        send_and_receive(port=self.port, baudrate=self.baudrate, hex_cmd='4d 4d 53 2c 31 0D', timeout=timeout)
        send_and_receive(port=self.port, baudrate=self.baudrate, hex_cmd='4d 44 53 2c 30 0D', timeout=timeout)
        send_and_receive(port=self.port, baudrate=self.baudrate, hex_cmd='4d 4d 53 2c 31 0D', timeout=timeout)
        return send_and_receive(port=self.port, baudrate=self.baudrate, hex_cmd='5a 52 43 0D', timeout=20)


    def JETAmode(self, timeout=2):
        """切换到JETA模式"""
        send_and_receive(port=self.port, baudrate=self.baudrate, hex_cmd='4d 4d 53 2c 32 0D', timeout=2)
        send_and_receive(port=self.port, baudrate=self.baudrate, hex_cmd='46 43 53 2c 31 2c 31 0D', timeout=5)
        send_and_receive(port=self.port, baudrate=self.baudrate, hex_cmd='4d 43 48 2c 30 0D', timeout=5)
        send_and_receive(port=self.port, baudrate=self.baudrate, hex_cmd='5a 52 43 0D', timeout=20)
        return send_and_receive(port=self.port, baudrate=self.baudrate, hex_cmd='4a 43 53 2c 31 2c 31 2c 30 2c 32 0D', timeout=5) # 1Hz sample？


    def GetXYZ(self, timeout=0.5):
        """获取 x Lv y 三色坐标"""
        hex_data, ascii_data = send_and_receive(port=self.port, baudrate=self.baudrate, hex_cmd='4d 45 53 2c 31 0D', timeout=timeout)
        parts = ascii_data.split(',')
        x, y, lv = parts[3], parts[4], parts[5]
        return float(x), float(lv), float(y)
    

    def GetJETA(self, timeout=1, sepctrum = False):
        """获取 JETA 值"""
        if sepctrum:
            hex_data, ascii_data = send_and_receive(port=self.port, baudrate=self.baudrate, hex_cmd='4a 44 52 2c 31 0D', timeout=timeout)
            return ascii_data
        else:
            send_and_receive(port=self.port, baudrate=self.baudrate, hex_cmd='4d 45 53 2c 31 0D', timeout=5)
            hex_data, ascii_data = send_and_receive(port=self.port, baudrate=self.baudrate, hex_cmd='4a 44 52 2c 30 0D', timeout=timeout)
            parts = ascii_data.split(',')
            return abs(float(parts[3]))



class CA410Sensor(CA527Sensor):
    def JETAmode(self, timeout=2):
        """切换到JETA模式"""
        send_and_receive(port=self.port, baudrate=self.baudrate, hex_cmd='4d 4d 53 2c 30 0D', timeout=2)
        send_and_receive(port=self.port, baudrate=self.baudrate, hex_cmd='46 4d 53 2c 31 0D', timeout=2)
        send_and_receive(port=self.port, baudrate=self.baudrate, hex_cmd='4a 43 53 2c 31 2c 39 0D', timeout=1) # 采样间隔1Hz 数量512
        hex_data, ascii_data = send_and_receive(port=self.port, baudrate=self.baudrate, hex_cmd='5a 52 43 0D', timeout=5) # 校0
        return ascii_data
    
    def GetJETA(self, timeout=5):
        send_and_receive(port=self.port, baudrate=self.baudrate, hex_cmd='4d 45 53 2c 31 0D') #执行测量
        hex_data, ascii_data = send_and_receive(port=self.port, baudrate=self.baudrate, hex_cmd='4a 44 52 2c 31 2c 30 0D', timeout=timeout) #获取JIETA值
        parts = ascii_data.split(',')
        return abs(float(parts[3]))
        
    def GetJETASepctrum(self, timeout=12):
        send_and_receive(port=self.port, baudrate=self.baudrate, hex_cmd='4d 45 53 2c 31 0D') #执行测量
        hex_data, ascii_data = send_and_receive(port=self.port, baudrate=self.baudrate, hex_cmd='4a 44 52 2c 31 2c 30 0D') #获取JIETA值
        # 6-65Hz Step 1Hz
        __, ascii_data = send_and_receive(port=self.port, baudrate=self.baudrate, hex_cmd='4a 44 52 2c 31 2c 31 0D', timeout=timeout) #获取JEITA频谱
        return [float(x) for x in ascii_data.split(',')[2:]]



class F500Sensor:
    """
    面向对象封装的传感器接口。
    """
    def __init__(self, port='COM8', baudrate=115200):
        self.port = port
        self.baudrate = baudrate

    def GetXYZ(self, timeout=1):
        recvAck = send_and_receive(port=self.port, baudrate=self.baudrate, hex_cmd='55 aa 88 77 f5 00', timeout=timeout, response_size=64)

        # 按照C代码的顺序提取字节（倒序取）
        tX = bytes([recvAck[13], recvAck[12], recvAck[11], recvAck[10],
                    recvAck[9], recvAck[8], recvAck[7], recvAck[6]])
        tY = bytes([recvAck[21], recvAck[20], recvAck[19], recvAck[18],
                    recvAck[17], recvAck[16], recvAck[15], recvAck[14]])
        tZ = bytes([recvAck[29], recvAck[28], recvAck[27], recvAck[26],
                    recvAck[25], recvAck[24], recvAck[23], recvAck[22]])

        # 解析成 double（小端字节序）
        pX  = struct.unpack('<d', tX)[0]
        pY  = struct.unpack('<d', tY)[0]
        pZ  = struct.unpack('<d', tZ)[0]

        return pX, pY, pZ

    def GetFMA(self, timeout=1):
        recvAck = send_and_receive(port=self.port, baudrate=self.baudrate, hex_cmd='55 aa 88 77 f6 00', timeout=timeout, response_size=64)

        # 按照C代码的顺序提取字节（倒序取）
        tFma = bytes([recvAck[9], recvAck[8], recvAck[7], recvAck[6]])
        # 按照C的float解码（通常是小端序）
        pFma = struct.unpack('<f', tFma)[0]

        return pFma

    def GetJETA(self, timeout=1):
        recvAck = send_and_receive(port=self.port, baudrate=self.baudrate, hex_cmd='55 aa 88 77 f7 00', timeout=timeout, response_size=64)

        # 按照C代码的顺序提取字节（倒序取）
        tFma = bytes([recvAck[9], recvAck[8], recvAck[7], recvAck[6]])
        # 按照C的float解码（通常是小端序）
        pJETA = struct.unpack('<f', tFma)[0]

        return pJETA


    # 0 开始显示，1 锁定显示
    def LockSensor(self, flag, timeout=1):
        if(flag):
            recvAck = send_and_receive(port=self.port, baudrate=self.baudrate, hex_cmd='55 aa 88 77 1f 01', timeout=timeout, response_size=64)
        else:
            recvAck = send_and_receive(port=self.port, baudrate=self.baudrate, hex_cmd='55 aa 88 77 1f 00', timeout=timeout, response_size=64)

        return recvAck
    

    def calibrate_sensor(self, timeout=7):
        """发送校准命令"""
        return 0

    def XYZmode(self, timeout=2):
        """切换到色坐标 (XYZ) 模式"""
        return 0


    def JETAmode(self, timeout=2):
        """切换到JETA模式"""
        return 0
    


class EYE200Sensor:
    """
    面向对象封装的眼动传感器接口。
    """
    def __init__(self, port='COM8', baudrate=115200):
        self.port = port
        self.baudrate = baudrate

    def calibrate_sensor(self, timeout=7):
        """发送校准命令"""
        return send_and_receive(port=self.port, baudrate=self.baudrate, hex_cmd='5A 52 43 20 2B 27 5C 72 27 0D', timeout=timeout)

    def XYZmode(self, timeout=2):
        """切换到色坐标 (XYZ) 模式"""
        return send_and_receive(port=self.port, baudrate=self.baudrate, hex_cmd='4D 44 53 2C 37 2B 27 5C 72 27 0D', timeout=timeout)


    def JETAmode(self, timeout=2):
        """切换到JETA模式"""
        return send_and_receive(port=self.port, baudrate=self.baudrate, hex_cmd='4D 44 53 2C 38 2B 27 5C 72 27 0D', timeout=timeout)


    def GetXYZ(self, timeout=1):
        """获取 XYZ 三色坐标"""
        hex_data, ascii_data = send_and_receive(port=self.port, baudrate=self.baudrate, hex_cmd='4D 45 53 2C 5B 31 5D 0D', timeout=timeout)
        match = re.search(r'P1\s*([\d.]+);([\d.]+);([\d.]+)\r', ascii_data)
        if not match:
            raise ValueError(f"Unexpected response for XYZ: {ascii_data}")
        x_val, y_val, z_val = match.group(1, 2, 3)
        return float(x_val), float(y_val), float(z_val)

    def GetJETA(self, timeout=2):
        """获取 JETA 值"""
        hex_data, ascii_data = send_and_receive(port=self.port, baudrate=self.baudrate, hex_cmd='4A 54 41 0D', timeout=timeout)
        match = re.search(r'P1\s*([\d.]+)', ascii_data)
        if not match:
            raise ValueError(f"Unexpected response for JETA: {ascii_data}")
        return float(match.group(1))
